#include <dlmalloc.h>
#include <memory.h>
#include <malloc.h>
#include <init.h>

struct dlallocator_priv {
	mspace m;
};

static int dlallocator_startup(struct memory_zone *zone)
{
	struct dlallocator_priv *priv;
	mspace m;

	m = create_mspace(0, zone);
	if (!m) 
		return -ENOMEM;
	priv = mspace_malloc(m, sizeof(struct dlallocator_priv));
	if (!priv)
		return -ENOMEM;
	priv->m = m;
	zone_set_priv(zone, priv);
	if (mspace_set_footprint_limit(m, zone->length) != zone->length) 
		return -EFAULT;
	
	return 0;
}

static void *dlallocator_malloc(struct memory_zone *zone, size_t bytes)
{
	struct dlallocator_priv *priv = zone_get_priv(zone);
	void *p;
	
	p = mspace_malloc(priv->m, bytes);
	if (!p)
		return ERR_PTR(-ENOMEM);
	return p;
}

static void dlallocator_free(struct memory_zone *zone, void* mem)
{
	struct dlallocator_priv *priv = zone_get_priv(zone);
	
	mspace_free(priv->m, mem);
}

static void *dlallocator_calloc(struct memory_zone *zone, size_t n_elements, size_t elem_size)
{
	struct dlallocator_priv *priv = zone_get_priv(zone);
	void *p;
	
	p = mspace_calloc(priv->m, n_elements, elem_size);
	if (!p)
		return ERR_PTR(-ENOMEM);
	return p;
}

static void *dlallocator_realloc(struct memory_zone *zone, void* mem, size_t newsize)
{
	struct dlallocator_priv *priv = zone_get_priv(zone);
	void *p;
	
	p = mspace_realloc(priv->m, mem, newsize);
	if (!p)
		return ERR_PTR(-ENOMEM);
	return p;
}

static void *dlallocator_realloc_in_place(struct memory_zone *zone, void* mem, size_t newsize)
{
	struct dlallocator_priv *priv = zone_get_priv(zone);
	void *p;
	
	p = mspace_realloc_in_place(priv->m, mem, newsize);
	if (!p)
		return ERR_PTR(-ENOMEM);
	return p;
}

static void *dlallocator_memalign(struct memory_zone *zone, size_t alignment, size_t bytes)
{
	struct dlallocator_priv *priv = zone_get_priv(zone);
	void *p;
	
	p = mspace_memalign(priv->m, alignment, bytes);
	if (!p)
		return ERR_PTR(-ENOMEM);
	return p;
}

static size_t dlallocator_footprint(struct memory_zone *zone)
{
	struct dlallocator_priv *priv = zone_get_priv(zone);
	
	return mspace_footprint(priv->m);
}

static size_t dlallocator_max_footprint(struct memory_zone *zone)
{
	struct dlallocator_priv *priv = zone_get_priv(zone);
	
	return mspace_max_footprint(priv->m);
}

static size_t dlallocator_footprint_limit(struct memory_zone *zone)
{
	struct dlallocator_priv *priv = zone_get_priv(zone);
	
	return mspace_footprint_limit(priv->m);
}

static struct memory_allocator dlallocator = {
	.name				= "dlallocator",
	.startup			= dlallocator_startup,
	.malloc				= dlallocator_malloc,
	.free				= dlallocator_free,
	.calloc				= dlallocator_calloc,
	.realloc			= dlallocator_realloc,
	.realloc_in_place	= dlallocator_realloc_in_place,
	.memalign			= dlallocator_memalign,
	.footprint			= dlallocator_footprint,
	.max_footprint		= dlallocator_max_footprint,
	.footprint_limit	= dlallocator_footprint_limit,
};

static int dlallocator_init(void)
{
	return memory_allocator_add(&dlallocator);
}
earliest0_initcall(dlallocator_init);

